﻿// -------------------------------------------------------------------------
//    @FileName         :    NFOnlineAcc.cpp
//    @Author           :    gaoyi
//    @Date             :    2025-04-03
//    @Email			:    445267987@qq.com
//    @Module           :    NFOnlineAcc
//
// -------------------------------------------------------------------------

#include "NFOnlineAcc.h"

#include <NFServerComm/NFServerCommon/NFIServerMessageModule.h>

#include "ConstantDescEx.h"
#include "NFOnlineAccMgr.h"
#include "NFOnlineRole.h"
#include "proto_svr_onlinesvr.nanopb.h"
#include "NFLogicCommon/NFPublicDefine.h"
#include "NFOnlineRoleService.h"
#include "proto_svr_common.nanopb.h"
#include "NFComm/NFCore/NFServerTime.h"
#include "Trans/NFTransAccCreateRole.h"
#include "Trans/NFTransAccLogin.h"
#include "Trans/NFTransSaveAcc.h"

class NFIServerMessageModule;

NFOnlineAcc::NFOnlineAcc()
{
    if (NFShmMgr::Instance()->GetCreateMode() == EN_OBJ_MODE_INIT)
    {
        CreateInit();
    }
    else
    {
        ResumeInit();
    }
}

NFOnlineAcc::~NFOnlineAcc()
{
}

int NFOnlineAcc::CreateInit()
{
    m_uid = 0;
    m_zoneId = 0;
    m_state = ONLINE_ACC_NONE;
    m_isInitRoleList = false;
    m_loginTransTime = 0;
    m_addRoleLockTime = 0;
    SetTimer(500, 0, 0, 0, 0, 0);
    return 0;
}

int NFOnlineAcc::ResumeInit()
{
    return 0;
}

int NFOnlineAcc::InitData(const proto_ff::tbRoleList* pPbClass)
{
    CHECK_NULL(this->GetCacheId(), pPbClass);
    m_stData.FromPb(*pPbClass);
    m_stPageStorage.Init(m_uid, EN_BPT_STORAGE, pPbClass->storage_data());
    return 0;
}

int NFOnlineAcc::SaveData(proto_ff::tbRoleList* pPbClass)
{
    CHECK_NULL(this->GetCacheId(), pPbClass);
    m_stData.ToPb(pPbClass);
    m_stPageStorage.MakeCSData(pPbClass->mutable_storage_data());
    return 0;
}

int NFOnlineAcc::OnTimer(int timeId, int callCount)
{
    SaveToDb(60, TRANS_SAVEROLEDETAIL_NORMAL, false);
    if (GetState() == ONLINE_ACC_ACTIVE)
    {
        if (CanDelete())
        {
            SetState(ONLINE_ACC_EXIT);

            NFOnlineAccMgr::Instance()->RemoveOnlineAcc(this);
        }
    }

    return 0;
}

bool NFOnlineAcc::CanDelete()
{
    if (IsInLockTime() || HasRoleOnline() || IsNeedSave() || IsSavingDb())
    {
        return false;
    }
    return true;
}

int NFOnlineAcc::SendTransToDb(int iReason)
{
    NFTransSaveAcc* pTrans = NFTransSaveAcc::CreateTrans();
    CHECK_NULL_WF(GetUid(), pTrans, "CreateTrans failed, NFTransSaveDetailDB used num:{} all num:{}", NFTransSaveAcc::GetStaticUsedCount(), NFTransSaveAcc::GetStaticItemCount());
    int iRetCode = pTrans->SaveDb(this, iReason);
    CHECK_ERR_AND_FIN_TRANS(iRetCode, pTrans, "SaveDb failed, iReason:{}", iReason);
    return 0;
}

RoleOne* NFOnlineAcc::FindRoleOne(uint64_t ullRoleId)
{
    auto iter = m_stData.role_data.role_list.find(ullRoleId);
    if (iter != m_stData.role_data.role_list.end())
    {
        return &iter->second;
    }

    return nullptr;
}

RoleOne* NFOnlineAcc::FindRoleOne(uint64_t ullRoleId, RoleData& roleData)
{
    auto iter = roleData.role_list.find(ullRoleId);
    if (iter != roleData.role_list.end())
    {
        return &iter->second;
    }

    return nullptr;
}

RoleOne* NFOnlineAcc::FindDelRoleOne(uint64_t ullRoleId, RoleData& roleData)
{
    auto iter = roleData.del_role.find(ullRoleId);
    if (iter != roleData.del_role.end())
    {
        return &iter->second;
    }

    return nullptr;
}

int NFOnlineAcc::AddRole(RoleOne& stRoleOne)
{
    int iRetCode = AddRole(stRoleOne, m_stData.role_data);
    if (iRetCode == 0)
    {
        MarkDirty();
    }

    return iRetCode;
}

int NFOnlineAcc::AddRole(RoleOne& stRoleOne, RoleData& roleData)
{
    int iMaxRoleNum = ConstantDescEx::GetValue(proto_ff::EN_CONST_ROLE_LIST_NUM);
    iMaxRoleNum = iMaxRoleNum > 0 ? iMaxRoleNum : roleData.role_list.max_size();

    if (roleData.role_list.size() < roleData.role_list.max_size()
        && (int)roleData.role_list.size() < iMaxRoleNum)
    {
        roleData.role_list[stRoleOne.role_id] = stRoleOne;
        return 0;
    }

    return proto_ff::ERR_CODE_ACCOUNT_ROLE_IS_FULL;
}

bool NFOnlineAcc::HasRole(uint64_t ullRoleId)
{
    RoleOne* pstRoleOne = FindRoleOne(ullRoleId);
    if (pstRoleOne)
    {
        return true;
    }

    return false;
}

bool NFOnlineAcc::HasRole(uint64_t ullRoleId, RoleData& roleData)
{
    RoleOne* pstRoleOne = FindRoleOne(ullRoleId, roleData);
    if (pstRoleOne)
    {
        return true;
    }

    return false;
}

bool NFOnlineAcc::HasDelRole(uint64_t ullRoleId, RoleData& roleData)
{
    RoleOne* pstRoleOne = FindDelRoleOne(ullRoleId, roleData);
    if (pstRoleOne)
    {
        return true;
    }

    return false;
}

int NFOnlineAcc::DeleteRole(uint64_t ullRoleId)
{
    int iTimeNow = NF_ADJUST_TIMENOW();
    int iCDHour = ConstantDescEx::GetValue(proto_ff::EN_CONST_DEL_ROLE_CD_HOUR);
    iCDHour = iCDHour > 0 ? iCDHour : 24;

    if (iTimeNow < m_stData.role_data.last_del_time + HOUR(iCDHour))
    {
        return proto_ff::ERR_CODE_ACCOUNT_DEL_ROLE_TIME_LIMITE;
    }

    auto pRoleOne = FindRoleOne(ullRoleId);
    if (pRoleOne)
    {
        AddDelRole(*pRoleOne);
        m_stData.role_data.role_list.erase(ullRoleId);

        m_stData.role_data.last_del_time = iTimeNow;
        MarkDirty();
    }
    else
    {
        return proto_ff::ERR_CODE_PLAYER_DONT_HAVE_ROLE;
    }

    return 0;
}

int NFOnlineAcc::AddDelRole(RoleOne& stRoleOne)
{
    if (!IsDelRoleFull())
    {
        m_stData.role_data.del_role[stRoleOne.role_id] = stRoleOne;
    }
    else
    {
        LOGSVR_ERROR("del role list is fulled!");
        return INVALID_ID;
    }

    return 0;
}

int NFOnlineAcc::TryAccLogin(const proto_ff::Proto_Zone2OnLineLoginReq& req, int fromBusId, int transId)
{
    if (m_state == ONLINE_ACC_EXIT)
    {
        LOG_ERR(m_uid, -1, "OnlineAcc already exit, uid: {}, zoneId: {}, transId: {} fromBusId: {}", m_uid, m_zoneId, transId, fromBusId);
        return ERR_CODE_ACC_ROLE_EXIT;
    }

    int iRetCode = 0;
    if (HasInitRoleList() == false)
    {
        iRetCode = GetRoleList(req.zone_id(), req.player_uin(), req.platform_type(), fromBusId, transId);
        if (iRetCode == 0)
        {
            if (m_state == ONLINE_ACC_NONE)
            {
                SetState(ONLINE_ACC_LOGIN);
            }
        }

        return iRetCode;
    }

    bool hasRoleOnline = false;
    for (auto iter = m_stData.role_data.role_list.list_begin(); iter != m_stData.role_data.role_list.list_end(); iter++)
    {
        uint64_t roleId = iter->first;
        NFOnlineRole* pRole = NFOnlineRoleService::GetOnlineRole(roleId);
        if (pRole)
        {
            hasRoleOnline = true;
            break;
        }
    }

    if (hasRoleOnline)
    {
        switch (GetState())
        {
            case ONLINE_ACC_ACTIVE:
            {
                iRetCode = SendRoleList(fromBusId, transId);
                break;
            }
            case ONLINE_ACC_EXIT:
            {
                iRetCode = ERR_CODE_ACC_ROLE_EXIT;
                LOG_ERR(0, iRetCode, "OnlineAcc already exit, uid: {}, zoneId: {}, transId: {} fromBusId: {}", m_uid, m_zoneId, transId, fromBusId);
                break;
            }
            default:
            {
                iRetCode = ERR_CODE_ACC_SYS;
                LOG_ERR(0, iRetCode, "OnlineAcc state error, state: {}, uid: {}, zoneId: {}, transId: {} fromBusId: {}", m_state, m_uid, m_zoneId, transId, fromBusId);
                break;
            }
        }
    }
    else
    {
        iRetCode = SendRoleList(fromBusId, transId);
    }

    return iRetCode;
}

int NFOnlineAcc::GetRoleList(int zoneId, uint64_t playerUin, int platformType, int fromBusId, int transId)
{
    NFTransAccLogin* pTrans = NFTransAccLogin::CreateTrans();
    CHECK_NULL_WF(0, pTrans, "CreateTrans failed, NFTransAccLogin used num:{} all num:{}", NFTransAccLogin::GetStaticUsedCount(), NFTransAccLogin::GetStaticItemCount());
    int iRetCode = pTrans->StartGetRoleList(zoneId, playerUin, platformType, fromBusId, transId);
    CHECK_ERR_AND_FIN_TRANS(iRetCode, pTrans, "StartGetRoleList failed, zoneId: {}, playerUin: {}, platformType: {}, fromBusId: {}, transId: {}", zoneId, playerUin, platformType, fromBusId, transId);
    return 0;
}

int NFOnlineAcc::SendRoleList(int fromBusId, int transId)
{
    proto_ff::Proto_OnLine2ZoneLoginRes res;
    res.set_zone_id(m_zoneId);
    res.set_player_uin(m_uid);
    res.set_result(0);
    m_stData.ToPb(res.mutable_role_list());

    for (auto iter = m_stData.role_data.role_list.list_begin(); iter != m_stData.role_data.role_list.list_end(); iter++)
    {
        uint64_t roleId = iter->first;
        NFOnlineRole* pRole = NFOnlineRoleService::GetOnlineRole(roleId);
        if (pRole)
        {
            res.add_online_role_list(roleId);
        }
    }

    FindModule<NFIServerMessageModule>()->SendTransToLogicServer(NF_ST_ONLINE_SERVER, fromBusId, E_ONLINE2ZONE_LOGIN_RES, res, m_uid, 0, transId);
    return 0;
}

int NFOnlineAcc::TryCreateRole(const proto_ff::Proto_Zone2OnLineCreateRoleReq& req, int fromBusId, int transId)
{
    if (m_state == ONLINE_ACC_EXIT)
    {
        LOG_ERR(m_uid, -1, "OnlineAcc already exit, uid: {}, zoneId: {}, transId: {} fromBusId: {}", m_uid, m_zoneId, transId, fromBusId);
        return ERR_CODE_ACC_ROLE_EXIT;
    }

    if (HasInitRoleList() == false)
    {
        LOG_ERR(m_uid, -1, "Role list not init, uid: {}, zoneId: {}, transId: {} fromBusId: {}", m_uid, m_zoneId, transId, fromBusId);
        return ERR_CODE_ACC_NOT_INIT;
    }

    if (m_stData.role_data.role_list.size() >= m_stData.role_data.role_list.max_size() || m_stData.role_data.role_list.size() >= ConstantDescEx::GetValue(proto_ff::EN_CONST_ROLE_LIST_NUM))
    {
        LOG_ERR(m_uid, ERR_CODE_ACCOUNT_ROLE_IS_FULL, "Role list is full, uid: {}, zoneId: {}, transId: {} fromBusId: {}", m_uid, m_zoneId, transId, fromBusId);
        return ERR_CODE_ACCOUNT_ROLE_IS_FULL;
    }

    if (GetAddRoleLockTime() > 0)
    {
        LOG_ERR(m_uid, ERR_CODE_HAS_OTHER_CLIENT_CREATE_ROLE, "has other client create role, uid: {}, zoneId: {}, transId: {} fromBusId: {}", m_uid, m_zoneId, transId, fromBusId);
        return ERR_CODE_HAS_OTHER_CLIENT_CREATE_ROLE;
    }

    SetAddRoleLockTime(NF_ADJUST_TIMENOW());

    NFTransAccCreateRole* pTrans = NFTransAccCreateRole::CreateTrans();
    CHECK_NULL_WF(0, pTrans, "CreateTrans failed, NFTransAccCreateRole used num:{} all num:{}", NFTransAccCreateRole::GetStaticUsedCount(), NFTransAccCreateRole::GetStaticItemCount());
    int iRetCode = pTrans->Init(m_zoneId, m_uid, req.platform_type(), fromBusId, transId, req);
    CHECK_ERR_AND_FIN_TRANS(iRetCode, pTrans, "Init failed, zoneId: {}, playerUin: {}, platformType: {}, fromBusId: {}, transId: {}", m_zoneId, m_uid, req.platform_type(), fromBusId, transId);
    iRetCode = pTrans->StartInsertName();
    CHECK_ERR_AND_FIN_TRANS(iRetCode, pTrans, "StartInsertName failed, zoneId: {}, playerUin: {}, platformType: {}, fromBusId: {}, transId: {}", m_zoneId, m_uid, req.platform_type(), fromBusId, transId);
    return 0;
}

int NFOnlineAcc::SyncLogin(int logicSvrId, const proto_ff::RoleOne& roleOne, bool isRegister)
{
    uint64_t roleId = roleOne.role_id();

    if (GetState() == ONLINE_ACC_EXIT)
    {
        LOG_ERR(roleId, -1, "OnlineAcc already exit, uid: {}, zoneId: {}, roleId: {}, logicSvrId: {}", m_uid, m_zoneId, roleId, logicSvrId);
    }
    else
    {
        if (GetState() != ONLINE_ACC_ACTIVE)
        {
            SetState(ONLINE_ACC_ACTIVE);
        }
    }

    auto pRoleOne = FindRoleOne(roleId);
    CHECK_NULL(roleId, pRoleOne);
    pRoleOne->FromPb(roleOne);

    NFOnlineRole* pOnlineRole = NFOnlineRoleService::GetOnlineRole(roleId);
    if (!pOnlineRole)
    {
        pOnlineRole = NFOnlineRoleService::CreateOnlineRole(roleId);
        CHECK_NULL_WF(roleId, pOnlineRole, "CreateOnlineRole failed, roleId: {}", roleId);
        pOnlineRole->SetZoneId(m_zoneId);
        pOnlineRole->SetUin(m_uid);
    }

    pOnlineRole->Init(logicSvrId, roleOne);

    return 0;
}

int NFOnlineAcc::ClearOnlineData(int logicSvrId, uint64_t roleId)
{
    if (roleId == 0)
    {
        return 0;
    }

    NFOnlineRole* pOnlineRole = NFOnlineRoleService::GetOnlineRole(roleId);
    if (pOnlineRole && logicSvrId > 0 && logicSvrId != pOnlineRole->GetLogicSvrId())
    {
        LOG_ERR(roleId, -1, "Role LogicSvrId Not Match, RoleId:{} OnlineRole LogicSvrId:{} Ntf LogicSvrId:{}", roleId, pOnlineRole->GetLogicSvrId(), logicSvrId);
        return -1;
    }

    NFOnlineRoleService::RemoveOnlineRole(pOnlineRole);

    m_stData.role_data.last_off_role = roleId;
    MarkDirty();
    return 0;
}

bool NFOnlineAcc::IsInLockTime()
{
    if (GetLoginTransTime() > 0 || GetAddRoleLockTime())
    {
        return true;
    }

    return false;
}

bool NFOnlineAcc::HasRoleOnline()
{
    for (auto iter = m_stData.role_data.role_list.begin(); iter != m_stData.role_data.role_list.end(); ++iter)
    {
        uint64_t roleId = iter->first;
        NFOnlineRole* pOnlineRole = NFOnlineRoleService::GetOnlineRole(roleId);
        if (pOnlineRole)
        {
            return true;
        }
    }
    return false;
}

int NFOnlineAcc::BroadCast(int msgId, const google::protobuf::Message& xData)
{
    for (auto iter = m_stData.role_data.role_list.begin(); iter != m_stData.role_data.role_list.end(); ++iter)
    {
        uint64_t roleId = iter->first;
        NFOnlineRole* pOnlineRole = NFOnlineRoleService::GetOnlineRole(roleId);
        if (pOnlineRole)
        {
            FindModule<NFIServerMessageModule>()->SendTransToLogicServer(NF_ST_ONLINE_SERVER, pOnlineRole->GetLogicSvrId(), msgId, xData, roleId);
        }
    }
    return 0;
}
